﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Threading.Tasks;
using EncriptionLibrary;
using System.Net;
using System.IO;

namespace Goa
{
    class MultiServer
    {
        private IPAddress localhost;
        private List<Tuple<int, TcpClient, List<string>, Excercise>> connectedClients;
        //private Dictionary<int, TcpClient> patientDic = new Dictionary<int,TcpClient>();

        private string encrkey = "5h589h5h";
        Form1 form;

        public MultiServer(Form1 form)
        {
            this.form = form;
            connectedClients = new List<Tuple<int, TcpClient, List<string>, Excercise>>();
            Thread thread = new Thread(startServer);
            thread.Start();
        }

        public void startServer()
        {
            
            localhost = IPAddress.Parse("127.0.0.1");

            TcpListener listener = new TcpListener(localhost, 1333);
            listener.Start();

            while (true)
            {
                Console.WriteLine("MultiServer is waiting for connection");
                TcpClient client = listener.AcceptTcpClient();
                Console.WriteLine("TcpClient");
                Thread thread = new Thread(HandleClientThread);
                Console.WriteLine("New Thread");
                thread.Start(client);
                Console.WriteLine("New Thread started");
            }
        }

        public void HandleClientThread(object obj)
        {
            TcpClient client = obj as TcpClient;
            string value = ReadMessage(client);
            int patientnumber = Convert.ToInt32(value);
            connectedClients.Add(new Tuple<int, TcpClient, List<string>, Excercise>(patientnumber, client, new List<string>(), new Excercise(this, client)));
            //patientDic.Add(patientnumber,client);
            SendMessage(client,"Login ok with login number: " + patientnumber.ToString());
            Console.WriteLine("New Client with number: " + patientnumber);
            //Console.WriteLine("Added new entry,{1} to patientDic size is now: {0}", patientDic.Count,patientnumber);

            bool done = false;
            while (!done)
            {

                string received = ReadMessage(client);
                if (received != null)
                {
                    if (received.Equals("terminate"))
                    {
                        //patientDic.Remove(patientnumber);
                        //Remove client
                        for (int i = 0; i < connectedClients.Count; i++)
                        {
                            if (connectedClients[i].Item1 == patientnumber)
                            {
                                connectedClients.RemoveAt(i);
                                form.disconnectedClient(patientnumber);
                                done = true;
                            }
                        }
                        break;
                    }
                    else {
                        form.updateMY(patientnumber,received);
                    }
                    Console.WriteLine("Received from Patient{1}: {0}", received, patientnumber);
                    //Console.WriteLine("Sending to patient: {0}", patientnumber);
                    //Console.WriteLine("patientDic: " + patientDic.Values);
                    //SendMessage(client, "OK");
                }
              
            }
            form.disconnectedClient(patientnumber);
            client.Close();
            Console.WriteLine("Connection closed for patient: {0}",patientnumber);
        }

        public string ReadMessage(TcpClient client)
        {
            string message = null;
            try
            {
                if (client.GetStream().DataAvailable)
                {
                    byte[] buffer = new byte[512];
                    int totalRead = 0;
                    try
                    {
                        do
                        {
                            try
                            {
                                int read = client.GetStream().Read(buffer, totalRead, buffer.Length - totalRead);
                                totalRead += read;
                            }
                            catch (IOException) { }
                        }
                        while (client.GetStream().DataAvailable);
                        message = encryption(Encoding.Unicode.GetString(buffer, 0, totalRead), encrkey, enEnCryptDecrypt.DECRYPT);
                    }
                    catch (InvalidOperationException) { }
                }
            }
            catch(InvalidOperationException)
            {
                for(int i = 0; i < connectedClients.Count; i++)
                {
                    if (connectedClients[i].Item2 == client)
                    {
                        connectedClients.RemoveAt(i);
                    }
                }

            }
            return message;
        }

        public void SendMessage(TcpClient client, string message)
        {
            try
            {
                //Console.WriteLine(clientNumber);
                string encrMessage = encryption(message, encrkey, enEnCryptDecrypt.ENCRYPT);
                byte[] bytes = Encoding.Unicode.GetBytes(encryption(message, encrkey, enEnCryptDecrypt.ENCRYPT));
                client.GetStream().Write(bytes, 0, bytes.Length);
            }
            catch (KeyNotFoundException knf)
            {
                Console.WriteLine("Key of TcpClient not found: {0}", knf);
            }
            catch (IOException)
            {

            }
            catch (InvalidOperationException)
            {

            }
        }

        public static string encryption(String Message, String Key, enEnCryptDecrypt EnC)
        {
            int messageCount, keyCount, value = 0;

            StringBuilder strRetVal = new StringBuilder();
            if (Message.Length > 0 && Key.Length > 0)
            {
                try
                {
                    for (messageCount = 0, keyCount = 0; messageCount < Message.Length; messageCount++, keyCount++)
                    {
                        if (keyCount >= Key.Length) keyCount = 0; // Rotates the Key string counter to Zero when character value is traversed till end

                        if (EnC == enEnCryptDecrypt.ENCRYPT)
                            value = (Message[messageCount]) + (Key[keyCount]); //When Encryption is required we add ascii value of key with Message
                        else
                            value = (Message[messageCount]) - (Key[keyCount]); // When Decryption is required we subtract ascii value of key with Message

                        strRetVal.Append((char)(value)); // Here we convert the ascii value to character and Append that to Stringbuilder
                    }
                }
                catch (Exception ex)
                {
                    // Clears the StringBuilder and Append Error Message in it.
                    strRetVal.Remove(0, strRetVal.Length);
                    strRetVal.Append("Error in Ecrypting:-" + ex.Message);
                }
            }
            else
            {
                // Append Return message with proper valid string required.
                strRetVal.Append("Enter valid Message and Key");
            }

            return strRetVal.ToString(); //Returns the Processed String.
        }

        public List<Tuple<int, TcpClient, List<string>, Excercise>> getClients()
        {
            return connectedClients;
        }
    }

}
